#include "../include/ThreadCache.h"
#include "../include/CentralCache.h"
//申请内存对象
void *ThreadCache::Allocate(size_t size)
{
	//在申请内存对象时，要向上进行对齐
	assert(size <= MAX_BYTES);
	//算出对齐后的大小，如果桶中没有没有与size对应的对象，我们就向中心cache获取
	size_t alignSize = GetObjSizeClass::RoundUp(size);
	//算出当前size应该映射到哪个哈希桶
	size_t index = GetObjSizeClass::Index(size);
	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();
	}
	else
	{
		//从中心cache获取对象，获取对齐数大小的内存
		return FetchFromCentralCache(index, alignSize);
	}
}

//释放内存对象
void ThreadCache::Deallocate(void *ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);
	//找到size映射到桶的哪个位置，然后将ptr插入
	size_t index = GetObjSizeClass::Index(size);
	_freeLists[index].Push(ptr);

	//当链表长度大于一次批量申请的内存时就开始还一段list给CentralCache
	if (_freeLists[index].Size() >= _freeLists[index].GetMaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}
}
void ThreadCache::ListTooLong(FreeList &list, size_t size)
{
	void *start = nullptr;
	void *end = nullptr;
	//取一段内存出来还给下一层，start指针记录链表起始位置
	list.PopRange(start, end, list.GetMaxSize());
	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

//从中心cache获取对象
void *ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	//批量地获取一些对象
	//慢开始调节算法，一开始只会获取1个，此后每次获取都会比上一次多1个，但是不会超过NumMoveSize(size)的上限
	//防止一次要太多用不完的情况
	size_t batchNum = (std::min)(_freeLists[index].GetMaxSize(), GetObjSizeClass::NumMoveSize(size));

	//要一块空间，这一块空间包含batchNum个size大小的对象
	//这些对象会构成链表，用两个指针指向这块链表的头和尾
	void *start = nullptr;
	void *end = nullptr;
	//实际要了多少个
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum >= 1);
	//根据实际要了多少个，来管理这些对象
	if (actualNum == 1)
	{
		//如果只是要了1个，那么直接返回给调用层
		assert(start == end);
	}
	else
	{
		//如果要了多个，那么我们只需要返回1个，剩下的放到哈希桶中
		//所以这里用到GetNextObj(start)，把start对象的下一个对象到end对象这个链表放到哈希桶中
		_freeLists[index].PushRange(GetNextObj(start), end, actualNum - 1);
	}
	//慢开始调节算法
	if (_freeLists[index].GetMaxSize() == batchNum)
	{
		_freeLists[index].GetMaxSize() += 1;
	}

	return start;
}